package forum.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;

import forum.shared.Delimiters;
import forum.shared.ServerResponse;

public class MainScreen extends Screen{
	private Button registerBtn;
	private Button searchBtn;
	private Button loginBtn;
	private Button logoffBtn;
	private Button addPostBtn;
	private Button usersBtn;
	private Button adminBtn;
	private Label greetLbl;
	private FlexTable mainTable;
	private FlexTable buttonsTable;
	private HorizontalPanel contentPanel;
	private Tree postsTree;
	private SearchScreen SearchSc;
	private TreeItem treeRoot;
	private Label errorLbl;
	private Button refreshBtn;
	
	/**
	 * default constructor.
	 */
	public MainScreen(){
		createComponents();
		setStyle();
		setTabOrder();
		setActions();
		setupScreenDisplay();
	}
	
	/**
	 * init all the components.
	 */
	protected void createComponents(){
		greetLbl = new Label();
		registerBtn = new Button("Register");
		searchBtn = new Button("Search");
		loginBtn = new Button("Login");
		logoffBtn = new Button("Logoff");
		addPostBtn = new Button("+ Post");
		usersBtn = new Button("Online Users");
		refreshBtn = new Button("Refresh");
		adminBtn = new Button("Admin Panel");
		mainTable = new FlexTable();
		buttonsTable = new FlexTable();
		contentPanel = new HorizontalPanel();
	    postsTree = new Tree();
	    SearchSc = null;
	    errorLbl = new Label();
	}
	
	/**
	 * setting the style to all the components.
	 */
	protected void setStyle(){
		errorLbl.addStyleName("errorLabel");
		errorLbl.setHorizontalAlignment(HasAlignment.ALIGN_CENTER);
		errorLbl.setVisible(false);
		mainTable.addStyleName("blueStyleTable");
		buttonsTable.addStyleName("buttonsTable");
		contentPanel.addStyleName("horizontalPanel");
		mainTable.setSize("100%", "100%");
		buttonsTable.getCellFormatter().setHorizontalAlignment(0, 0, HasAlignment.ALIGN_CENTER);
		//setting the style to the fields.
		
		//setting the buttons style.
		registerBtn.addStyleName("button");
		loginBtn.addStyleName("button");
		logoffBtn.addStyleName("button");
		searchBtn.addStyleName("button");
		addPostBtn.addStyleName("button");
		usersBtn.addStyleName("button");
		refreshBtn.addStyleName("button");
		adminBtn.addStyleName("button");
	}
	
	protected void setTabOrder(){
		registerBtn.setTabIndex(0);
		loginBtn.setTabIndex(1);
		searchBtn.setTabIndex(2);
		addPostBtn.setTabIndex(3);
	}
	
	protected void setActions(){
	    registerBtn.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				new RegisterScreen().setupDisplayInMainPanel();
			}
		});

	    registerBtn.addKeyPressHandler(new KeyPressHandler() {	
			@Override
			public void onKeyPress(KeyPressEvent event) {
				if (event.getCharCode() == KeyCodes.KEY_ENTER) {
					new RegisterScreen().setupDisplayInMainPanel();
			    }
			}
		}); 
	    
	    this.searchBtn.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				if (SearchSc == null)
					SearchSc = new SearchScreen();
				SearchSc.setupDisplayInMainPanel();
			}
		});
	    
	    this.searchBtn.addKeyPressHandler(new KeyPressHandler() {
			@Override
			public void onKeyPress(KeyPressEvent event) {
				if (event.getCharCode() == KeyCodes.KEY_ENTER) {
					if (SearchSc == null)
						SearchSc = new SearchScreen();
					SearchSc.setupDisplayInMainPanel();
			    }
			}
		});
	    this.loginBtn.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				new LoginScreen().setupDisplayInMainPanel();
			}
		});
	    
	    this.loginBtn.addKeyPressHandler(new KeyPressHandler() {
			@Override
			public void onKeyPress(KeyPressEvent event) {
				if (event.getCharCode() == KeyCodes.KEY_ENTER)
					new LoginScreen().setupDisplayInMainPanel();
			}
		});
	    
	    this.addPostBtn.addClickHandler(new ClickHandler() {	
			@Override
			public void onClick(ClickEvent event) {
				new AddPostDialog().setupDisplayInMainPanel();
			}
		});
	    
	    this.addPostBtn.addKeyPressHandler(new KeyPressHandler() {
			@Override
			public void onKeyPress(KeyPressEvent event) {
				if (event.getCharCode() == KeyCodes.KEY_ENTER)
					new AddPostDialog().setupDisplayInMainPanel();
			}
		});
	    
	    this.logoffBtn.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				logoff();
			}
		});
	    
	    this.usersBtn.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				new OnlineUsersDialog().setupDisplayInMainPanel();	
			}
		});
	    
	    this.refreshBtn.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				new MainScreen().setupDisplayInMainPanel();
			}
		});
	    
	    this.adminBtn.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				new AdminScreen().setupDisplayInMainPanel();
			}
		});
	    
	}
	
	@Override
	public String getInput(){
		/*String error = "";
        if (!error.equals("")) {
        	Window.alert(error);
  	      	return null;
        }*/
        return null;
	}

	/**
	 * composing the screen on top of the mainPanel which controls the body of the html.
	 * @param mainPanel
	 */
	public void setupDisplayInMainPanel() {
		Panel mainPanel = MainPanel.get();
		mainPanel.clear();
		/*mainPanel.add(image);
		mainPanel.add(time);*/
		mainPanel.add(greetLbl);
		mainPanel.add(buttonsTable);
		mainPanel.add(errorLbl);
		mainPanel.add(contentPanel);
		
		// set greeting label
		if (SessionData.getInstance().getPermissions().equals(UserPermissions.GUEST))
			greetLbl.setText("Welcome Guest!");
		else {
			greetLbl.setText("Welcome " + 
					SessionData.getInstance().getCurrentUser() + "!");
			buttonsTable.setWidget(1, 1, addPostBtn);
		}
		
		// admin panel button
		if (SessionData.getInstance().getPermissions().equals(UserPermissions.ADMIN))
			buttonsTable.setWidget(0, 5, adminBtn);
		
		// set buttons table
		buttonsTable.setWidget(0, 0, registerBtn);
		if (SessionData.getInstance().getPermissions().equals(UserPermissions.GUEST))
			buttonsTable.setWidget(0, 1, loginBtn);
		else
			buttonsTable.setWidget(0, 1, logoffBtn);
		buttonsTable.setWidget(0, 2, searchBtn);
		buttonsTable.setWidget(0, 3, usersBtn);
		buttonsTable.setWidget(1, 0, refreshBtn);
		
		buttonsTable.getCellFormatter().setHorizontalAlignment(1, 2, HasAlignment.ALIGN_RIGHT);
	}
	
	/**
	 * setting up the display of this screen.
	 */
	protected void setupScreenDisplay(){
	    postsTree.addItem("loading");
		
		FlexTable treeTable = new FlexTable();
		treeTable.addStyleName("blueStyleTable");
		treeTable.getCellFormatter().setStyleName(0, 0, "blueStyleTd");
		treeTable.setWidget(0, 0, postsTree);
		//contentPanel.add(postsTree);
		contentPanel.add(treeTable);
		setTreeContent();
	}
	
	/**
	 * the function to do when entering the forum main view.
	 */
	private void setTreeContent(){
		ForumServiceAsync forumSvc = GWT.create(ForumService.class);
		// Set up the callback object.
	    AsyncCallback<ServerResponse> callback = new AsyncCallback<ServerResponse>() {
	    	public void onFailure(Throwable caught) {
	    		errorLbl.setText("Communication error. Please retry later.");
	    		errorLbl.setVisible(true);
	    	}
	    	//On successful communication.
			@Override
			public void onSuccess(ServerResponse response) {
				if (response!=null && response.hasExecuted()) {
					TreeItem root = createRoot();
					treeRoot = new TreeItem();
					decodeView(response.getResponse(), treeRoot);
					SessionData.getInstance().setPostsCount(treeRoot.getChildCount());
				    root.setUserObject(new TreeUserObject("-1", new TextArea()));
				    treeRoot.setUserObject(new TreeUserObject("-1", new TextArea()));
					String postId = SessionData.getInstance().getCurrentSearch();
					SessionData.getInstance().setCurrentSearch(null);
				    if(postId != null){
				    	TreeItem res = searchPostId(treeRoot, postId);
				    	enterTreeItemsWithinRangeToView(root);
				    	root.setState(true);
				    	postsTree.removeItems();
				    	postsTree.addItem(root);
						if(res == null){
							Window.alert("searchResult wasn't found - maybe already been deleted.");
							return;
						}
						else{
							res.setState(true);
							res.setSelected(true);
							postsTree.setSelectedItem(res);
						}
				    }
				    else{
				    	enterTreeItemsWithinRangeToView(root);
				    	root.setState(true);
				    	postsTree.removeItems();
				    	postsTree.addItem(root);
				    }
				    errorLbl.setVisible(false);
	            } 
				else {
					errorLbl.setText(response!=null ? 
                			response.getResponse() : "Unknown Error...");
                	errorLbl.setVisible(true);
	            }
			}
	    };
		// Make the call to the forum service.
	    forumSvc.sendMsg("Tree", callback);
	}
	/**
	 * this function changes the display according to the paging.
	 * @param root the tree root display.
	 */
	private void enterTreeItemsWithinRangeToView(TreeItem root) {
		int count = treeRoot.getChildCount() - 1;
		int range = SessionData.getInstance().getPagingRange(); 
		if(range == -1){ //all the tree.
			for(int i=count; i>=0; i--)
				root.addItem(treeRoot.getChild(i));
		}
		else{
			int start = 0;
			int end = 0;
			if(start+range-1 > count)
				end = count;
			else
				end = start + range - 1;
			//reverse the order.
			int temp = start;
			start = count - end;
			end = count - temp;
			
			for(int i=end; i>=start; i--)
				root.addItem(treeRoot.getChild(i));
		}
	}
	
	
	/**
	 * creates a single post view.
	 * @param user the author of the post.
	 * @param header the title of the post.
	 * @param content the content of the post.
	 * @param id the post id.
	 * @return the view of the post.
	 */
	private TreeItem createTreeItem(String id, String user, String header, String content){
		final int postId = Integer.parseInt(id);
		TreeItem result = new TreeItem();
		TextArea contentBox = new TextArea();
		contentBox.setText(content);
		contentBox.addStyleName("treeItemContent");
		result.setText(user + " : " + header);
		result.setUserObject(new TreeUserObject(id, contentBox));
		FlexTable postTable = new FlexTable();
		
		FlexTable buttonsTable = new FlexTable();
		//buttonsTable.addStyleName("buttonsTable");
		
		Button delete = new Button("Delete");
		Button modify = new Button("Modify");
		Button addReply = new Button("Reply");
		
		delete.addStyleName("smallButton");
		modify.addStyleName("smallButton");
		addReply.addStyleName("smallButton");
		
		postTable.setWidget(0, 0, contentBox);
		
		// set buttons visability
		UserPermissions permission = SessionData.getInstance().getPermissions();
		if (permission.compareTo(UserPermissions.LOGGED) > 0 
				|| user.equals(SessionData.getInstance().getCurrentUser())){ //Admin || Moderator || Aothur
			contentBox.setEnabled(true);
			buttonsTable.setWidget(0, 1, modify);
			buttonsTable.setWidget(0, 2, delete);
			TreeUserObject to = (TreeUserObject)result.getUserObject();
			result.setUserObject(to);
			delete.addClickHandler(new DeleteHandler(to, this, delete));
			modify.addClickHandler(new ModifyHandler(to, this, modify));
		}
		else {
			contentBox.setEnabled(false);
		}
		
		// set visability of reply button to every user but GUEST
		if (permission.compareTo(UserPermissions.GUEST) > 0 ) {
			buttonsTable.setWidget(0, 0, addReply);
			addReply.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent event) {
					new AddPostDialog(postId).setupDisplayInMainPanel();
				}
			});
		}
			
		//postTable.getCellFormatter().setStyleName(0, 0, "blueStyleTd");
		postTable.getCellFormatter().setHorizontalAlignment(0, 0, HasAlignment.ALIGN_LEFT);
		
		postTable.setWidget(2, 0, buttonsTable);
		result.setStyleName("treeItem");
		result.addItem(postTable);
		return result;
	}
	
	/**
	 * this is the action taken when the delete button of one of the posts have been pressed. 
	 */
	public void deletePost(String postId, String newContent, final Button bt) {
		/*postsTree.removeItems();
		postsTree.addItem("modifying please wait...");*/
		bt.setEnabled(false);	
		
		ForumServiceAsync forumSvc = GWT.create(ForumService.class);
		// Set up the callback object.
	    AsyncCallback<ServerResponse> callback = new AsyncCallback<ServerResponse>() {
	    	public void onFailure(Throwable caught) {
	    		errorLbl.setText("Communication error. Please retry later.");
	    		errorLbl.setVisible(true);
	    		bt.setEnabled(true);
	    		//setTreeContent();
	    	}
	    	//On successful communication.
			@Override
			public void onSuccess(ServerResponse response) {
				if (response!=null && response.hasExecuted()) {
					setTreeContent();
	            } 
				else {
					errorLbl.setText(response!=null ? 
                			response.getResponse() : "Unknown Error...");
                	errorLbl.setVisible(true);
	            }
			}
	    };
		// Make the call to the forum service.
	    String input = "Delete" + Delimiters.MSG_PARTS + postId;
	    forumSvc.sendMsg(input, callback);
	}
	/**
	 * this is the action taken when the modify button of one of the posts have been pressed. 
	 */
	public void modifyPost(String postId, String newContent, final Button bt) {
		/*postsTree.removeItems();
		postsTree.addItem("modifying please wait...");*/
		bt.setEnabled(false);	
		
		ForumServiceAsync forumSvc = GWT.create(ForumService.class);
		// Set up the callback object.
	    AsyncCallback<ServerResponse> callback = new AsyncCallback<ServerResponse>() {
	    	public void onFailure(Throwable caught) {
	    		errorLbl.setText("Communication error. Please retry later.");
	    		errorLbl.setVisible(true);
	    		bt.setEnabled(true);
	    		//setTreeContent();
	    	}
	    	//On successful communication.
			@Override
			public void onSuccess(ServerResponse response) {
				if (response!=null && response.hasExecuted()) {
					//setTreeContent();
					bt.setEnabled(true);
					errorLbl.setVisible(false);
	            } 
				else {
					errorLbl.setText(response!=null ? 
                			response.getResponse() : "Unknown Error...");
                	errorLbl.setVisible(true);
	            }
			}
	    };
		// Make the call to the forum service.
	    String input = "Modify" + Delimiters.MSG_PARTS + postId + 
    		Delimiters.MSG_PARTS + newContent;
	    forumSvc.sendMsg(input, callback);
	}
	
	private TreeItem createRoot(){
		TreeItem result = new TreeItem();
		FlexTable postTable = new FlexTable();
		postTable.addStyleName("treeRoot");
		Label title = new Label("Welcome to BANNANA FORUM");
		title.addStyleName("labelcell");
		postTable.setWidget(0, 0, title);
		postTable.getCellFormatter().setStyleName(0, 0, "blueStyleTd");
		postTable.getCellFormatter().setHorizontalAlignment(0, 0, HasAlignment.ALIGN_CENTER);
		result.setWidget(postTable);		
		return result;
	}
	
	/**
	 * Receives an encoding describing the forum tree.<br>
	 * It decodes the description and returns the tree representation
     * in a TreeItem instance - the root of the tree.
	 *
	 * @return The tree representing the forum.
	 */
	private void decodeView(String encodedView, TreeItem root) {
		String tPost = getNextPost(encodedView); 
		while(tPost != null){
			String author = "";
			String header = "";
			String content = "";
			String id = "-1";
			int i = 0;
			int j = 0;
			i = encodedView.indexOf(Delimiters.DELIM, 1);
			id = encodedView.substring(j+1, i);
			tPost = tPost.substring(i+1);
			i = tPost.indexOf(Delimiters.DELIM, 0);
			author = tPost.substring(0, i);
			j = i;
			i = tPost.indexOf(Delimiters.DELIM, j+1);
			header = tPost.substring(j+1, i);
			j = i;
			i = tPost.indexOf(Delimiters.DELIM, j+1);
			content = tPost.substring(j+1, i);
			i++; //The last delim
			TreeItem current = createTreeItem(id, author, header, content);			
			j = encodedView.indexOf(Delimiters.DELIM, 1);
			i = encodedView.indexOf(Delimiters.POST_CH_END, tPost.length()+j+1);
			encodedView = encodedView.substring(i+1);
			i = tPost.indexOf(Delimiters.REPLIES_CH);
			if(i != -1){	//There are replies.
				String replies = tPost.substring(i+1, tPost.length()-1);
				decodeView(replies, current);
			}
			root.addItem(current);
			tPost = getNextPost(encodedView);
		}
	}
	
	/**
	 * @param encodedView the string which represents all the remaining posts.
	 * @return the string of the next post.
	 */
	private String getNextPost(String encodedView){
		if(encodedView.length() == 0) return null;
		String id = "-1";
		int i = 0;
		int j = 0;
		i = encodedView.indexOf(Delimiters.DELIM, 1);
		id = encodedView.substring(j+1, i);
		j = i;
		i = encodedView.indexOf(Delimiters.POST_CH_PRE_END + id + Delimiters.POST_CH_END);
		return encodedView.substring(0, i);
	}
	/**
	 * Opens the requested post in the tree.
	 * 
	 * @param postId the postId of the required post.
	 * @param searchScreen the search Screen obj.
	 */
	public void openSeacrhHit(int postId, SearchScreen searchScreen) {
		this.SearchSc = searchScreen;
		SessionData.getInstance().setCurrentSearch(""+postId);
		this.setupDisplayInMainPanel();
	}

	/**
	 * 
	 * @param root the TreeItem to search in.
	 * @param postId the postId to search.
	 * @return the TreeItem that matches the search, null otherwise.
	 */
	private TreeItem searchPostId(TreeItem root, String postId) {		
		TreeUserObject uo = (TreeUserObject)root.getUserObject();
		root.setUserObject(uo);
		if(uo == null) return null;
		String currentId = uo.getPostId();
		if(currentId.equals(postId)){ //first check the current node
			return root;
		}
			
		else{	//not in the root so check the children. 
			int children = root.getChildCount();
			for(int i=0; i<children; i++){
				TreeItem result = searchPostId(root.getChild(i), postId);
				if(result != null) {
					root.setState(true);
					return result;
				}
			}
		}
		return null;	//not in the root or its children.
	}
	
	private void logoff() {
		ForumServiceAsync forumSvc = GWT.create(ForumService.class);
		
		// Set up the callback object.
	    AsyncCallback<ServerResponse> callback = new AsyncCallback<ServerResponse>() {
	    	public void onFailure(Throwable caught) {
	    		errorLbl.setText("Communication error. Please retry later.");
	    		errorLbl.setVisible(true);
	    		new MainScreen().setupDisplayInMainPanel();
	    	}
	    	//On successful communication.
			@Override
			public void onSuccess(ServerResponse response) {
				if (response!=null && response.hasExecuted()) {
					SessionData.getInstance().setCurrentUser("");
					SessionData.getInstance().setPermissions(UserPermissions.GUEST);
					errorLbl.setVisible(false);
					new MainScreen().setupDisplayInMainPanel();
	            } 
				else {
					errorLbl.setText(response!=null ? 
                			response.getResponse() : "Unknown Error...");
                	errorLbl.setVisible(true);
	            }
			}
	    };
	    
		// Make the call to the forum service.
    	forumSvc.sendMsg("Logoff", callback);
	}
}
